﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.VisualStudio.TeamFoundation;

namespace CoryCissellMikeParks.Parkware_AutoShelve
{
    public class TfsAutoShelve
    {
        /// <summary>
        /// the timer object
        /// </summary>
        private Timer _timer = null;
        private int _timerInterval = 0;
        private WorkspaceInfo _wi = null;
        private string _workingDirectory = null;

        /// <summary>
        /// Initializes a new instance of the <see cref="TfsAutoShelve"/> class.
        /// </summary>
        public TfsAutoShelve()
        {
            this.BaseShelvesetName = Resources.ExtensionName;

            CreateTimer();

            IsRunning = false;
        }

        /// <summary>
        /// 
        /// </summary>
        public delegate void TfsAutoShelveExecution(TfsAutoShelveEventArgs e);

        public delegate void TfsAutoShelveTimerStart();

        public delegate void TfsAutoShelveTimerStop();

        public delegate void TfsAutoShelveWorkSpaceDiscovery(bool workSpaceDiscovered);

        /// <summary>
        /// Occurs after an attempt to create a shelveset.
        /// </summary>
        public event TfsAutoShelveExecution OnTfsAutoShelveExecutation;

        public event TfsAutoShelveTimerStart OnTfsAutoShelveTimerStart;

        public event TfsAutoShelveTimerStop OnTfsAutoShelveTimerStop;

        public event TfsAutoShelveWorkSpaceDiscovery OnTfsAutoShelveWorkSpaceDiscovery;

        /// <summary>
        /// Gets or sets the base name of the shelveset.
        /// </summary>
        /// <value>The name of the base shelveset.</value>
        public string BaseShelvesetName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets a value indicating whether the timer is running.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if the timer is running; otherwise, <c>false</c>.
        /// </value>
        public bool IsRunning
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the tfsExt from dte2 on init.
        /// </summary>
        /// <value>The tfsExt from dte2.</value>
        public TeamFoundationServerExt tfsExt 
        { 
            get; 
            set; 
        }

        /// <summary>
        /// Gets or sets the timer interval in minutes.
        /// </summary>
        /// <value>The timer interval.</value>
        public int TimerInterval
        {
            get
            {
                return _timerInterval;
            }
            set
            {
                bool wasRunning = this.IsRunning;

                // don't stop it if it wasn't running
                if (wasRunning)
                    StopTimer();

                _timerInterval = value;

                // don't start it if it wasn't already running
                if (wasRunning)
                    StartTimer();
            }
        }

        /// <summary>
        /// Gets or sets the working directory. 
        /// Determines if the directory is a mapped TFS workspace and raise the OnTfsAutoShelveWorkSpaceDiscovery event.
        /// </summary>
        /// <value>The working directory.</value>
        public string WorkingDirectory
        {
            get
            {
                return _workingDirectory;
            }
            set
            {
                _workingDirectory = value;

                if (!string.IsNullOrEmpty(value))
                {
                    // if a solution was opened that was in a directory not mapped
                    // to a TFS workspace then we need to notify the package.
                    // the timer will be disabled.
                    _wi = Workstation.Current.GetLocalWorkspaceInfo(value);

                    if (this.OnTfsAutoShelveWorkSpaceDiscovery != null)
                        OnTfsAutoShelveWorkSpaceDiscovery(_wi != null);
                }
            }
        }

        /// <summary>
        /// Gets the timer period. Converts the timer interval (minutes) to milliseconds.
        /// </summary>
        /// <value>The timer period.</value>
        private int TimerPeriod
        {
            get { return 1000 * (this.TimerInterval * 60); }
        }

        /// <summary>
        /// Creates the shelve set with the specified options.
        /// </summary>
        public void CreateShelveSet()
        {
            TimerCallback callBack = GetAutoShelveCallback();

            callBack.Invoke(new object());
        }

        /// <summary>
        /// Gets the auto shelve callback delegate. This is the delegate that will be called when the timer
        /// 
        /// </summary>
        /// <returns></returns>
        public TimerCallback GetAutoShelveCallback()
        {
            return new TimerCallback((state) =>
            {
                //if we don't have a mapped TFS workspace then don't do anything.
                if (_wi == null)
                    return;

                SaveShelveset(_wi.ServerUri.AbsoluteUri);
            });
        }

        public void SaveShelveset(string _uri)
        {
            TfsAutoShelveEventArgs args = new TfsAutoShelveEventArgs();

            try
            {
                TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(new Uri(_uri));

                VersionControlServer vcs = tpc.GetService(typeof(VersionControlServer)) as VersionControlServer;

                WorkspaceInfo[] wiAll = Workstation.Current.GetAllLocalWorkspaceInfo();

                foreach (WorkspaceInfo info in wiAll)
                {

                    try
                    {
                        if (info.MappedPaths.Count() > 0)
                        {
                            Workspace ws = vcs.GetWorkspace(info);

                            PendingChange[] pc = ws.GetPendingChanges();

                            string shelvesetName = this.BaseShelvesetName + " - " + info.Name;

                            Shelveset ss = new Shelveset(vcs, shelvesetName.Length > 64 ? shelvesetName.Substring(0, 63) : shelvesetName, info.OwnerName);

                            args.ShelvesetCount += pc.Count();

                            ss.Comment = string.Format("Shelved by " + Resources.ExtensionName + ". Items in shelveset: {0}", pc.Count().ToString());

                            ws.Shelve(ss, pc, ShelvingOptions.Replace);
                        }
                    }
                    catch (Exception)
                    {
                    }
                }

                if (OnTfsAutoShelveExecutation != null)
                    OnTfsAutoShelveExecutation(args);
            }
            catch (Exception e)
            {
                args.ExecutionException = e;

                if (OnTfsAutoShelveExecutation != null)
                    OnTfsAutoShelveExecutation(args);
            }
        }

        /// <summary>
        /// Starts the timer.
        /// </summary>
        public void StartTimer()
        {
            _timer.Change(0, this.TimerPeriod);

            IsRunning = true;

            if (OnTfsAutoShelveTimerStart != null)
                OnTfsAutoShelveTimerStart();
        }

        /// <summary>
        /// Stops the timer.
        /// </summary>
        public void StopTimer()
        {
            _timer.Change(Timeout.Infinite, Timeout.Infinite);

            IsRunning = false;

            if (OnTfsAutoShelveTimerStop != null)
                OnTfsAutoShelveTimerStop();
        }

        /// <summary>
        /// Starts and stops the timer.
        /// </summary>
        public void ToggleTimerRunState()
        {
            if (IsRunning)
            {
                // stop the timer callback from firing
                StopTimer();
            }
            else
            {
                // start the timer
                StartTimer();
            }
        }

        /// <summary>
        /// Creates the timer object is a "stopped" state.
        /// </summary>
        private void CreateTimer()
        {
            try
            {
                AutoResetEvent autoEvent = new AutoResetEvent(false);

                this._timer = new Timer(GetAutoShelveCallback(), autoEvent, Timeout.Infinite, Timeout.Infinite);
            }
            catch (Exception)
            {
            }
        }

        /// <summary>
        /// Stops the timer and raises the OnTfsAutoShelveWorkSpaceDiscovery with an argument of False.
        /// </summary>
        public void Terminate()
        {
            StopTimer();

            if (this.OnTfsAutoShelveWorkSpaceDiscovery != null)
            {
                OnTfsAutoShelveWorkSpaceDiscovery(false);
            }
        }
    }
}